#include "kvm-common/kvm_hardware.h"
#include <unistd.h>
#include <stdio.h>
#include <cstring>
#include <stdlib.h>


#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <net/if.h>
#include <net/if_arp.h>
#include <arpa/inet.h>


#include <regex>
#include <string>
#include <fstream>
using namespace std;
#ifdef ENABLE_FORCE_HWINFO
const char * const REGEX_FILE = "/config/force_hwinfo";
/**
 * @brief 提取文件中的数据 格式key=val
 * 
 * @param fname 文件名
 * @param key 
 * @return string 返回值 没有找到或者其他错误为空
 */
static string regex_load(const char *fname, const char *key)
{
	if (!fname || !key)
		return "";

	std::ifstream ifs(fname);
	std::string orgstr;

	if (!ifs)
		return "";

	ifs.seekg(0, ifs.end);
	int filelen = ifs.tellg();
	ifs.seekg(0, ifs.beg);

	orgstr.resize(filelen);

	ifs.read(&orgstr[0], filelen);

	const regex reg("(\\w+)=([\\w-:\\.]+)");

	smatch match;

	string::const_iterator star = orgstr.begin();
	string::const_iterator end = orgstr.end();
	while (regex_search(star, end, match, reg)) {
		if (match[1] == key) {
			return match[2];
		}
		star = match[0].second;
	}
	return "";
}
#endif

std::string get_mtdPath()
{
    return "/dev/mtdblock4";
}
/**
*从本地flash获取SN号，地址为bdinfo分区的第16KB位置，前三个byte为校验码0xAA,0xBB,0xCC
*/

static FILE* fopen_has_flag(const char* path, const int offset)
{
    FILE* f = fopen(path, "rb");
    if (f == NULL) return NULL;

    SEEK_TO_OFFSET(f, offset);
    unsigned char head[3] = {0};
    if (fread(head, sizeof(unsigned char), sizeof(head), f) == 3 &&
         head[0] == 0xAA && head[1] == 0xBB && head[2] == 0xCC)
        return f;

    fclose(f);
    return NULL;
}


int get_perf_from_local(char* perf) //read perf test result string, max 128 bytes
{
    FILE *f = fopen_has_flag(get_mtdPath().c_str(), PERF_TEST_FLAG_OFFSET);
    if (f == NULL) return 2;
	fread(perf, 1, 128, f);
	fclose(f);
	return 0;
}


int get_sn_from_local(char* sn)
{
#ifdef ENABLE_FORCE_HWINFO
	string reg = regex_load(REGEX_FILE, "sn");
	if (reg != "") {
		strcpy(sn, reg.c_str());
		return 0;
	}
#endif

    FILE *f = fopen_has_flag(get_mtdPath().c_str(), SN_OFFSET);
    if (f == NULL) return 2;
	fread(sn, 1, 20, f);
	fclose(f);
	return 0;
}

int get_mac_from_local(char* mac)
{
#ifdef ENABLE_FORCE_HWINFO
	string reg = regex_load(REGEX_FILE, "mac");
	if (reg != "") {
		strcpy(mac, reg.c_str());
		return 0;
	}
#endif

    FILE *f = fopen_has_flag(get_mtdPath().c_str(), MAC_OFFSET);
    if (f == NULL) return 2;
    fread(mac, 1, 20, f);
    fclose(f);
    return 0;
}

int get_cpu_id(char* id)
{
    char temp_chipid[50] = {0};
    char temp_serial[30] = {0};

    FILE *f = popen("cat /proc/cpuinfo | grep Chipid | awk {'print $3'}", "r");
    fgets(temp_chipid, 50, f);
    temp_chipid[strlen(temp_chipid) - 1] = '\0';
    pclose(f);

    FILE *f2 = popen("cat /proc/cpuinfo | grep Serial | awk {'print $3'}", "r");
    fgets(temp_serial, 30, f2);
    temp_serial[strlen(temp_serial) - 1] = '\0';
    pclose(f2);

    strcpy(id, temp_serial);
    strcat(id, temp_chipid);

    return 0;
}

int get_if_mac(const char *ifdev, char *mac, int size)
{
	if (ifdev && mac && size == 0) {
		return -1;
	}

	char sysaddr[128];
	sprintf(sysaddr, "/sys/class/net/%s/address", ifdev);

	ifstream ifile(sysaddr);
	if(!ifile.good())
		return -2;
	string tmpstr;
	ifile >> tmpstr;

	if(tmpstr.size() > 0) {
		strcpy(mac,tmpstr.c_str());
		return 0;
	}

	return -3;
}

int get_mac(char *mac, int size)
{
	return get_if_mac("eth0", mac, size);
}

int get_ether_status(bool *isUp)
{
    FILE* f;
    char str_info[20] = {0};
    int ret = -1;
    if((f = fopen("/sys/class/net/" ETH_IF "/operstate", "r"))== NULL ||
        fgets(str_info, sizeof(str_info), f) == NULL)
        goto end;

    if (strcmp(str_info, "up") != 0 || strstr(str_info, "unknown") != 0)
        *isUp = true;
    else
        *isUp = false;

    ret = 0;

    end:
    pclose(f);
    return ret;
}

int get_if_ip(const char *ifdev, char *ip)
{
	if ((ifdev && ip) == 0) {
		return -1;
	}
	int sock;
	struct sockaddr_in sin;
	struct ifreq ifr;
	sock = socket(AF_INET, SOCK_DGRAM, 0);
	if (sock == -1) {
		return -2;
	}

	strncpy(ifr.ifr_name, ifdev, IFNAMSIZ);
	ifr.ifr_name[IF_NAMESIZE - 1] = 0;
	if (ioctl(sock, SIOCGIFADDR, &ifr) < 0) {
		return -3;
	}

	memcpy(&sin, &ifr.ifr_addr, sizeof(sin));
	strcmp(ip, inet_ntoa(sin.sin_addr));
	return 0;
}

int get_if_mask(const char *ifdev, char *ip)
{
	if ((ifdev && ip) == 0) {
		return -1;
	}
	int sock;
	struct sockaddr_in sin;
	struct ifreq ifr;
	sock = socket(AF_INET, SOCK_DGRAM, 0);
	if (sock == -1) {
		return -2;
	}

	strncpy(ifr.ifr_name, ifdev, IFNAMSIZ);
	ifr.ifr_name[IF_NAMESIZE - 1] = 0;
	if (ioctl(sock, SIOCGIFADDR, &ifr) < 0) {
		return -3;
	}

	memcpy(&sin, &ifr.ifr_netmask, sizeof(sin));
	strcmp(ip, inet_ntoa(sin.sin_addr));
	return 0;
}


int get_ether_ip(char *ip)
{
    return get_if_ip(ETH_IF,ip);
}

int get_version(char *version)
{
    char buff[200] = {0};
    FILE *f = fopen("/etc/version", "r");
    while(fgets(buff, 200, f) != NULL)
    {
        if (sscanf(buff, "cpu:%s", version) == 1)
        {
            if (version[strlen(version) - 1] == '\n')
               version[strlen(version) - 1] = '\0';
            fclose(f);
            return 0;
        }
    }
    fclose(f);
    return -1;
}

int get_model(char *model)
{
#ifdef ENABLE_FORCE_HWINFO
	string reg = regex_load(REGEX_FILE, "model");
	if (reg != "") {
		strcpy(model, reg.c_str());
		return 0;
	}
#endif

    /* strcpy(model, MODTYPE); */
    strcpy(model, "Q2Pro");
    return 0;
}
